package com.ttjj.service.navigation;

import cn.hutool.core.collection.CollectionUtil;
import com.ttjj.common.Constants;
import com.ttjj.entity.map.Coordinate;
import com.ttjj.entity.map.MapElement;
import com.ttjj.entity.map.MapModel;
import com.ttjj.entity.map.Position;
import com.ttjj.entity.navi.NextDirectionInfo;
import com.ttjj.entity.player.PlayReqVo;
import com.ttjj.entity.player.PlayerStateVo;
import com.ttjj.enums.*;
import lombok.extern.slf4j.Slf4j;
import org.testng.collections.Lists;
import org.testng.collections.Sets;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 地形分析
 */
@Slf4j
public class TerrainAnalyzer {
    public static String MEPlayerId;

    public static String TeamColor;

    private static PlayerStateVo ME = null;
    /**
     * 是否只有敌人携带动物
     */
    private static Boolean onlyEnemyHasAnimal = false;

    private static final List<List<String>> mapData = new ArrayList<>();
    private static final HashSet<Position> mudPosSet = new HashSet<>();
    /**
     * 障碍物
     */
    private static final HashSet<Position> obstaclePosSet = new HashSet<>();
    /**
     * 可挖掉障碍物
     */
    private static final HashSet<Position> changingObstaclePosSet = new HashSet<>();
    /**
     * 其它玩家
     */
    public static final TreeSet<PlayerStateVo> OTHER_PLAYER = new TreeSet<>(Comparator.comparing(PlayerStateVo::getRank));
    /**
     * 队友
     */
    public static final Set<String> FRIEND_PLAY = Sets.newHashSet();
    /**
     * 敌人拍讯
     */
    public static final TreeSet<PlayerStateVo> ENEMY_PLAYER_RANK = new TreeSet<>(Comparator.comparing(PlayerStateVo::getRank));
    /**
     * 我方人员排序
     */
    public static final TreeSet<PlayerStateVo> OWN_PLAYER_RANK = new TreeSet<>(Comparator.comparing(PlayerStateVo::getRank));


    private static final Object locker = new Object();
    /**
     * 可作为元素的坐标
     */
    private static final Set<String> DYNAMIC_MAP_ELEMENT = Sets.newHashSet(Lists.newArrayList(
            EnumBlockStrType.Mud,
            EnumBlockStrType.Rock,
            EnumBlockStrType.Tree,
            EnumBlockStrType.Waste,
            EnumBlockStrType.Ground
    ));

    public static List<List<String>> getMap() {
        synchronized (locker) {
            return mapData;
        }
    }

    /**
     * 初始化地图
     *
     * @param p
     */
    public static void initMap(PlayReqVo p) {
        MEPlayerId = p.getPlayerId();
        MapModel mapModel = new MapModel();
        // 没啥用
        mapModel.setMapId("BB");
        mapModel.setMapSize("l");
        // 初始化地图
        List<MapElement> mapElements = initMapElement();
        List<List<String>> map = p.getMap();
        for (int i = 0; i < map.size(); i++) {
            List<String> mapy = map.get(i);
            for (int j = 0; j < mapy.size(); j++) {
                String s = mapy.get(j);
                if (DYNAMIC_MAP_ELEMENT.contains(s)) {
                    MapElement mapElement = new MapElement(j * 50 + 25, i * 50 + 25, EnumBlockStrType.convertTo(s));
                    mapElements.add(mapElement);
                }
            }
        }
        mapModel.setElements(mapElements);
        // 重置地图
        reset(mapModel);
        // 添加其它玩家
        String playerId = p.getPlayerId();
        List<PlayerStateVo> playerStateVos = p.getPlayerStateVos();
        OTHER_PLAYER.clear();
        OWN_PLAYER_RANK.clear();
        ENEMY_PLAYER_RANK.clear();
        if (CollectionUtil.isEmpty(FRIEND_PLAY)) {
            for (PlayerStateVo playerStateVo : playerStateVos) {
                if (playerStateVo.getPlayerId().equals(MEPlayerId)) {
                    TeamColor = playerStateVo.getTeamColor();
                }
            }
            for (PlayerStateVo playerStateVo : playerStateVos) {
                String teamColor = playerStateVo.getTeamColor();
                if (teamColor.equals(TeamColor)) {
                    FRIEND_PLAY.add(playerStateVo.getPlayerId());
                }
            }
        }
        for (PlayerStateVo playerStateVo : playerStateVos) {
            String playerId1 = playerStateVo.getPlayerId();
            String teamColor = playerStateVo.getTeamColor();
            if (FRIEND_PLAY.contains(playerId1)) {
                OWN_PLAYER_RANK.add(playerStateVo);
            } else {
                if (!teamColor.equals(TeamColor)) {
                    ENEMY_PLAYER_RANK.add(playerStateVo);
                }
            }
            if (Objects.equals(playerId1, playerId)) {
                ME = playerStateVo;
            } else {
                OTHER_PLAYER.add(playerStateVo);
            }
        }
    }

    /**
     * 固定元素生成
     *
     * @return
     */
    private static List<MapElement> initMapElement() {
        List<MapElement> result = new ArrayList<>();
        MapElement farmHouse = new MapElement();
        farmHouse.setType(EnumBlockType.FarmHouse);
        farmHouse.setX(700);
        farmHouse.setY(400);
        result.add(farmHouse);
        MapElement chickenHouse = new MapElement();
        chickenHouse.setType(EnumBlockType.ChickenHouse);
        chickenHouse.setX(50);
        chickenHouse.setY(50);
        result.add(chickenHouse);
        MapElement rabbitHouse = new MapElement();
        rabbitHouse.setType(EnumBlockType.RabbitHouse);
        rabbitHouse.setX(1350);
        rabbitHouse.setY(50);
        result.add(rabbitHouse);
        MapElement pigHouse = new MapElement();
        pigHouse.setType(EnumBlockType.PigHouse);
        pigHouse.setX(50);
        pigHouse.setY(750);
        result.add(pigHouse);
        MapElement cattleHouse = new MapElement();
        cattleHouse.setType(EnumBlockType.CattleHouse);
        cattleHouse.setX(1350);
        cattleHouse.setY(750);
        result.add(cattleHouse);
        return result;
    }

    /**
     * 地图书重置
     *
     * @param map
     */
    public static void reset(MapModel map) {
        synchronized (locker) {
            mapData.clear();
            mudPosSet.clear();
            obstaclePosSet.clear();
            changingObstaclePosSet.clear();

            for (int i = 0; i < 16; ++i) {
                List<String> mapLine = new ArrayList<>();
                for (int j = 0; j < 28; ++j) {
                    mapLine.add("G");
                }
                mapData.add(mapLine);
            }

            for (MapElement mapElement : map.getElements()) {
                EnumBlockType blockType = mapElement.getType();
                String blockStr = EnumBlockStrType.convertFrom(blockType);
                if (blockType != EnumBlockType.FarmHouse && blockType != EnumBlockType.ChickenHouse && blockType != EnumBlockType.RabbitHouse && blockType != EnumBlockType.PigHouse && blockType != EnumBlockType.CattleHouse) {
                    Position pos = mapElement.toPosition();
                    ((List) mapData.get(pos.getX())).set(pos.getY(), blockStr);
                    if (blockType == EnumBlockType.Mud) {
                        mudPosSet.add(pos);
                    } else if (blockType != EnumBlockType.Ground) {
                        obstaclePosSet.add(pos);
                        if (blockType == EnumBlockType.Rock || blockType == EnumBlockType.Tree || blockType == EnumBlockType.Waste) {
                            changingObstaclePosSet.add(pos);
                        }
                    }
                } else {
                    List<Position> posSet = mapElement.getHousePosSet();

                    for (Position pos : posSet) {
                        ((List) mapData.get(pos.getX())).set(pos.getY(), blockStr);
                        obstaclePosSet.add(pos);
                    }
                }
            }
        }
    }

    public static String getBlockType(Position pos) {
        synchronized (locker) {
            return (String) ((List<?>) mapData.get(pos.getX())).get(pos.getY());
        }
    }

    /**
     * 是否是泥土
     *
     * @param pos
     * @return
     */
    public static boolean isInMud(Position pos) {
        synchronized (locker) {
            return mudPosSet.contains(pos);
        }
    }

    public static boolean isInMud(Coordinate cod, int radius) {
        synchronized (locker) {
            return isInMud(cod.getTopLeftCod(radius).toPosition()) ||
                    isInMud(cod.getTopRightCod(radius).toPosition()) ||
                    isInMud(cod.getDownLeftCod(radius).toPosition()) ||
                    isInMud(cod.getDownRightCod(radius).toPosition());
        }
    }

    /**
     * 是否重叠
     *
     * @param pos
     * @return
     */
    public static boolean isOverlap(Position pos) {
        synchronized (locker) {
            return pos.getX() < 0 || pos.getX() >= 16 || pos.getY() < 0 || pos.getY() >= 28 || obstaclePosSet.contains(pos);
        }
    }

    /**
     * 是否重叠
     *
     * @param pos
     * @return
     */
    public static boolean isOverlap(Position pos, boolean hasShovel) {
        synchronized (locker) {
            boolean b = hasShovel && obstaclePosSet.contains(pos) && !changingObstaclePosSet.contains(pos);
            if (b) {
                log.info("有铲子，只判断不可铲格子");
            }
            return pos.getX() < 0 || pos.getX() >= 16 || pos.getY() < 0 || pos.getY() >= 28
                    || (!hasShovel && obstaclePosSet.contains(pos))
                    || b;
        }
    }

    /**
     * 是否重叠（像素级）
     *
     * @param cod
     * @param radius
     * @return
     */
    public static boolean isOverlap(Coordinate cod, int radius) {
        synchronized (locker) {
            return cod.getX() - radius < 0 || cod.getX() + radius > 1400 || cod.getY() - radius < 0 || cod.getY() + radius > 800 || isOverlap(cod.getTopLeftCod(radius).toPosition()) || isOverlap(cod.getTopRightCod(radius).toPosition()) || isOverlap(cod.getDownLeftCod(radius).toPosition()) || isOverlap(cod.getDownRightCod(radius).toPosition());
        }
    }

    /**
     * 障碍无是否清理掉
     *
     * @param pos
     * @return
     */
    public static boolean isChangingObstacle(Position pos) {
        synchronized (locker) {
            return changingObstaclePosSet.contains(pos);
        }
    }

    /**
     * 障碍无是否清理掉(像素级)
     *
     * @param cod
     * @param radius
     * @return
     */
    public static boolean isChangingObstacle(Coordinate cod, int radius) {
        synchronized (locker) {
            return isChangingObstacle(cod.getTopLeftCod(radius).toPosition()) || isChangingObstacle(cod.getTopRightCod(radius).toPosition()) || isChangingObstacle(cod.getDownLeftCod(radius).toPosition()) || isChangingObstacle(cod.getDownRightCod(radius).toPosition());
        }
    }

    /**
     * 判断边角像素是否重叠
     *
     * @param cod
     * @param radius
     * @return
     */
    public static List<Position> getOverlapChangingObs(Coordinate cod, int radius) {
        List<Position> result = new ArrayList<>();
        List<Position> cornerPosSet = Arrays.asList(cod.getTopLeftCod(radius).toPosition(), cod.getTopRightCod(radius).toPosition(), cod.getDownLeftCod(radius).toPosition(), cod.getDownRightCod(radius).toPosition());
        synchronized (locker) {
            for (Position cornerPos : cornerPosSet) {
                if (isChangingObstacle(cornerPos)) {
                    result.add(cornerPos);
                }
            }
            return result;
        }
    }

    /**
     * 地形变换同步
     *
     * @param overlapList
     */
    public static void terrainChange(List<Position> overlapList) {
        synchronized (locker) {
            overlapList.forEach((pos) -> {
                obstaclePosSet.remove(pos);
                changingObstaclePosSet.remove(pos);
                ((List) mapData.get(pos.getX())).set(pos.getY(), "G");
            });
        }
    }

    /**
     * 获取障碍物
     *
     * @return
     */
    public static List<Position> getObstaclePosSet() {
        synchronized (locker) {
            return new ArrayList<>(obstaclePosSet);
        }
    }

    /**
     * 农场中心处
     *
     * @return
     */
    public static Coordinate getDeliverCod() {
        return new Coordinate(700, 400);
    }


    /**
     * 农场中心处
     *
     * @return
     */
    public static Coordinate getNearestDeliverCod(Coordinate playerCoodinate) {
        Coordinate farmer = new Coordinate(700, 400);
        int x = 700;
        int y = 400;
        EnumDirection horizontalDirectFrom = playerCoodinate.getHorizontalDirectFrom(farmer);
        EnumDirection verticalDirectFrom = playerCoodinate.getVerticalDirectFrom(farmer);
        if (EnumDirection.TOP == verticalDirectFrom) {
            // 地图中心占用4个坐标，700/400 对应左下角
            y -= 2 * Constants.getCommonLen();
        } else if (EnumDirection.DOWN == verticalDirectFrom) {
            y += Constants.getCommonLen();
        } else if (EnumDirection.LEFT == horizontalDirectFrom) {
            x -= 2 * Constants.getCommonLen();
        } else if (EnumDirection.RIGHT == horizontalDirectFrom) {
            x += Constants.getCommonLen();
        }
        return new Coordinate(x, y);
    }

    /**
     * 获取本人信息
     *
     * @return
     */
    public static PlayerStateVo getME() {
        return ME;
    }

    /**
     * 判断此位置能否交货
     *
     * @param cod
     * @param radius
     * @return
     */
    public static boolean isDeliverPos(Coordinate cod, int radius) {
        int scale = Constants.getCommonLen() + radius;
        Coordinate deliverCod = getDeliverCod();
        int distanceX = Math.abs(cod.getX() - deliverCod.getX());
        int distanceY = Math.abs(cod.getY() - deliverCod.getY());
        return distanceX < scale && distanceY == scale || distanceX == scale && distanceY < scale;
    }

    public static int getSpeedByCod(PlayerStateVo movingObj, Coordinate cod) {
        List<EnumPropType> holdingProps = movingObj.getHoldingProps();
        boolean hasShoe = holdingProps.contains(EnumPropType.Shoe);
        int speed = !hasShoe && isInMud(cod, movingObj.getRadius()) ? Constants.SPEED_M : Constants.SPEED_G;
        return speed;
    }

    /**
     * 规避其它车辆，选择方向
     */
    public static NextDirectionInfo avoidCrash(PlayerStateVo playerStateVo, Coordinate me, NextDirectionInfo nextDirectionInfo, List<PlayerStateVo> others, List<EnumDirection> canDirections) {
        if (CollectionUtil.isEmpty(others)) {
            others = new ArrayList<>(OTHER_PLAYER);
        }

        if (CollectionUtil.isEmpty(canDirections)) {
            canDirections = Arrays.stream(EnumDirection.values()).collect(Collectors.toList());
        }
        // 下一步前往的地点
        Coordinate dest = nextDirectionInfo.getDest();
        // 下一步速度
        int speedByCod = TerrainAnalyzer.getSpeedByCod(playerStateVo, me);
        // 当前选择的下一步方向
        EnumDirection nextDirection = nextDirectionInfo.getNextDirection();
        // 获取下一步坐标
        Coordinate meNextCoordinate = me.stepByDirect(nextDirection, speedByCod);
        // 判断是否碰撞
        PlayerStateVo other = null;
        List<Coordinate> otherNextCrashCoordinates = new ArrayList<>();
        Iterator<PlayerStateVo> iterator = others.iterator();
        while (iterator.hasNext()) {
            // 其它玩家
            other = iterator.next();
            // 其它玩家可能下一步坐标
//            List<Coordinate> otherNextCoordinates = other.getAllNextCoordinate();
//            for (Coordinate nextCoordinate : otherNextCoordinates) {
//                if (meNextCoordinate.isCrashWith(nextCoordinate)) {
//                    otherNextCrashCoordinates.add(nextCoordinate);
//                }
//            }

            Coordinate otherCoordinate = other.getCoordinate();
            int otherSpeed = TerrainAnalyzer.getSpeedByCod(other, otherCoordinate);
            if (meNextCoordinate.isCrashWith(otherCoordinate)) {
                otherNextCrashCoordinates.add(otherCoordinate.stepByDirect(other.getMovingDirect(), otherSpeed));
            }

            if (!otherNextCrashCoordinates.isEmpty()) {
                iterator.remove();
                break;
            }
        }
        if (!otherNextCrashCoordinates.isEmpty()) {
            if (!log.isDebugEnabled()) {
                log.info("触发规避逻辑,我当前位置：{}, 我方向：{}," +
                                "敌当前位置：{}, 敌当前方向：{}," +
                                "敌下步碰撞可能位置：{}",
                        me, nextDirectionInfo.getNextDirection(),
                        other.getCoordinate(), other.getMovingDirect(),
                        otherNextCrashCoordinates);
            }
            EnumDirection verticalDirectFrom = dest.getVerticalDirectFrom(me);
            EnumDirection horizontalDirectFrom = dest.getHorizontalDirectFrom(me);

            if (verticalDirectFrom == nextDirection) {
                // 遍历敌方全部可能撞到的移动方向
                for (Coordinate otherNextCoordinate : otherNextCrashCoordinates) {
                    // 判断是否是我主动碰撞，如果是，需要剔除这个移动方向
                    if (meNextCoordinate.isMyActiveCrash(otherNextCoordinate, nextDirection)) {
                        // 优先选取另一个方向的移动方向
                        if (horizontalDirectFrom != EnumDirection.STOP) {
                            meNextCoordinate = me.stepByDirect(horizontalDirectFrom, speedByCod);
                            nextDirectionInfo.setNextDirection(horizontalDirectFrom);
                        }

                        // 检测选择后的新方向是否会碰撞
                        if (meNextCoordinate.isCrashWith(otherNextCoordinate)) {
                            nextDirection.getCanDirections(canDirections);
                            horizontalDirectFrom.getCanDirections(canDirections);
                            // 判断剩下可选择的方向
                            Iterator<EnumDirection> iterator1 = canDirections.iterator();
                            while (iterator1.hasNext()) {
                                EnumDirection enumDirection = iterator1.next();
                                meNextCoordinate = me.stepByDirect(enumDirection, speedByCod);
                                // 会碰撞就剔除掉这个方向
                                if (meNextCoordinate.isCrashWith(otherNextCoordinate)) {
                                    iterator1.remove();
                                }
                                nextDirectionInfo.setNextDirection(enumDirection.random(canDirections));
                            }
//                            for (EnumDirection enumDirection: canDirections) {
//                                meNextCoordinate = me.stepByDirect(enumDirection, speedByCod);
//                                // 会碰撞就剔除掉这个方向
//                                if (meNextCoordinate.isCrashWith(otherNextCoordinate)) {
//                                    enumDirection.getCanDirections(canDirections);
//                                }
//                                nextDirectionInfo.setNextDirection(enumDirection.random(canDirections));
//                            }
                        }
                    }
                }

                return avoidCrash(playerStateVo, me, nextDirectionInfo, others, canDirections);
            } else if (horizontalDirectFrom == nextDirection) {
                // 遍历敌方全部可能撞到的移动方向
                for (Coordinate otherNextCoordinate : otherNextCrashCoordinates) {
                    // 判断是否是我主动碰撞，如果是，需要剔除这个移动方向
                    if (meNextCoordinate.isMyActiveCrash(otherNextCoordinate, nextDirection)) {
                        // 优先选取另一个方向的移动方向
                        if (verticalDirectFrom != EnumDirection.STOP) {
                            meNextCoordinate = me.stepByDirect(verticalDirectFrom, speedByCod);
                            nextDirectionInfo.setNextDirection(verticalDirectFrom);
                        }

                        // 检测选择后的新方向是否会碰撞
                        if (meNextCoordinate.isCrashWith(otherNextCoordinate)) {
                            nextDirection.getCanDirections(canDirections);
                            verticalDirectFrom.getCanDirections(canDirections);
                            // 判断剩下可选择的方向
                            Iterator<EnumDirection> iterator1 = canDirections.iterator();
                            while (iterator1.hasNext()) {
                                EnumDirection enumDirection = iterator1.next();
                                meNextCoordinate = me.stepByDirect(enumDirection, speedByCod);
                                // 会碰撞就剔除掉这个方向
                                if (meNextCoordinate.isCrashWith(otherNextCoordinate)) {
                                    iterator1.remove();
                                }
                                nextDirectionInfo.setNextDirection(enumDirection.random(canDirections));
                            }
//                            for (EnumDirection enumDirection: canDirections) {
//                                meNextCoordinate = me.stepByDirect(enumDirection, speedByCod);
//                                // 会碰撞就剔除掉这个方向
//                                if (meNextCoordinate.isCrashWith(otherNextCoordinate)) {
//                                    enumDirection.getCanDirections(canDirections);
//                                }
//                                nextDirectionInfo.setNextDirection(enumDirection.random(canDirections));
//                            }
                        }
                    }
                }

                return avoidCrash(playerStateVo, me, nextDirectionInfo, others, canDirections);
            }

        }
        if (nextDirectionInfo.getNextDirection() == EnumDirection.STOP) {
            Random random = new Random();
            if (random.nextInt(10) < 5) {
                nextDirectionInfo.setNextDirection(nextDirection.revert());
            }
        }
        return nextDirectionInfo;
    }
}
